#![allow(unused_imports)]

use config::ConfigManager;
use config::config::{Config, PluginParams};
use std::env;
use std::fs;
use std::io::Write;
use tempfile::{NamedTempFile, TempDir};

// Create a valid test configuration YAML string
fn create_valid_config_yaml() -> String {
    r#"
agent:
  listen_address: "127.0.0.1"
  listen_port: 8080
  uuid: "test-agent-001"

server:
  server_url: "https://attestation-server.example.com"
  timeout_seconds: 30
  tls:
    enabled: true
    cert_path: "/path/to/cert.pem"
    key_path: "/path/to/key.pem"

plugins:
  - name: "tpm-plugin"
    path: "/usr/lib/plugins/tpm.so"
    enabled: true
    params:
      attester_type: tpm
      tcti_config: "device:/dev/tpm0"
      event_log_path: "/sys/kernel/security/tpm0/binary_bios_measurements"
  
  - name: "ima-plugin"
    path: "/usr/lib/plugins/ima.so"
    enabled: true
    params:
      attester_type: ima
      tcti_config: "device:/dev/tpm0"
      log_path: "/sys/kernel/security/ima/ascii_runtime_measurements"
      ascii_runtime_measurements: true

schedulers:
  - name: "periodic-attestation"
    enabled: true
    cron_expression: "0 */6 * * *"
    initial_delay:
      min_seconds: 10
      max_seconds: 30

logging:
  level: "info"
  file: "/var/log/attestation-agent.log"
"#.to_string()
}

// Create a temporary configuration file
fn create_temp_config_file(content: &str) -> NamedTempFile {
    let mut file = NamedTempFile::new().expect("Failed to create temp file");
    file.write_all(content.as_bytes()).expect("Failed to write to temp file");
    file
}

#[tokio::test]
async fn test_config_loading_from_command_line_path() {
    let config_content = create_valid_config_yaml();
    let temp_file = create_temp_config_file(&config_content);
    let config_path = temp_file.path().to_str().unwrap();
    
    let config_manager = ConfigManager::new(config_path).await.expect("Failed to load config");
    
    // Verify the configuration has been correctly loaded
    assert_eq!(config_manager.get_config_path(), config_path);
    let config: Config = config_manager.get_config().await;
    assert_eq!(config.agent.listen_port, 8080);
    assert_eq!(config.server.server_url, "https://attestation-server.example.com");
    assert_eq!(config.plugins.len(), 2);
    assert_eq!(config.schedulers.len(), 1);
}

#[tokio::test]
async fn test_config_loading_priority() {
    // Create a temporary directory as the current directory
    let temp_dir = TempDir::new().expect("Failed to create temp dir");
    let original_dir = env::current_dir().expect("Failed to get current dir");
    
    // Switch to the temporary directory
    env::set_current_dir(&temp_dir).expect("Failed to change directory");
    
    // Create three configuration files with different contents
    let cmd_config = "agent:\n  listen_port: 1111\n  listen_address: \"0.0.0.0\"\nserver:\n  server_url: \"cmd\"\n  timeout_seconds: 10\nlogging:\n  level: \"info\"\n  file: \"log.txt\"\nplugins: []\nschedulers: []";
    let current_dir_config = "agent:\n  listen_port: 2222\n  listen_address: \"0.0.0.0\"\nserver:\n  server_url: \"current\"\n  timeout_seconds: 20\nlogging:\n  level: \"info\"\n  file: \"log.txt\"\nplugins: []\nschedulers: []";
    
    let cmd_file = create_temp_config_file(cmd_config);
    let cmd_path = cmd_file.path().to_str().unwrap();
    
    // Create agent_config.yaml in the current directory
    let current_file_path = temp_dir.path().join("agent_config.yaml");
    fs::write(&current_file_path, current_dir_config).expect("Failed to write current dir config");
    
    // Test 1: Command line arguments have higher priority than the current directory
    let config_manager = ConfigManager::new(cmd_path).await.expect("Failed to load config");
    let config: Config = config_manager.get_config().await;
    assert_eq!(config.agent.listen_port, 1111);
    assert_eq!(config.server.server_url, "cmd");
    
    // Test 2: If no command line arguments are provided, use the current directory
    let config_manager = ConfigManager::new("").await.expect("Failed to load config");
    let config: Config = config_manager.get_config().await;
    assert_eq!(config.agent.listen_port, 2222);
    assert_eq!(config.server.server_url, "current");
    
    // Restore the original directory
    env::set_current_dir(original_dir).expect("Failed to restore directory");
}

#[tokio::test]
async fn test_error_when_config_not_found() {
    // Use a non-existent path
    let result = ConfigManager::new("/path/does/not/exist").await;
    assert!(result.is_err());
    
    // Ensure that there is no agent_config.yaml file in the current directory
    let temp_dir = TempDir::new().expect("Failed to create temp dir");
    let original_dir = env::current_dir().expect("Failed to get current dir");
    env::set_current_dir(&temp_dir).expect("Failed to change directory");
    
    let result = ConfigManager::new("").await;
    assert!(result.is_err());
    
    env::set_current_dir(original_dir).expect("Failed to restore directory");
}

#[tokio::test]
async fn test_invalid_yaml_config() {
    // Create an invalid YAML configuration
    let invalid_yaml = "this is not valid yaml: { unclosed bracket: ";
    let temp_file = create_temp_config_file(invalid_yaml);
    let config_path = temp_file.path().to_str().unwrap();
    
    let result = ConfigManager::new(config_path).await;
    assert!(result.is_err());
}

#[tokio::test]
async fn test_missing_required_fields() {
    // Configuration missing required fields
    let incomplete_config = r#"
agent:
  listen_address: "127.0.0.1"
  # Missing listen_port
server:
  # Missing server_url
  timeout_seconds: 30
plugins: []
schedulers: []
# Missing logging section
"#;
    let temp_file = create_temp_config_file(incomplete_config);
    let config_path = temp_file.path().to_str().unwrap();
    
    let result = ConfigManager::new(config_path).await;
    assert!(result.is_err());
}

#[tokio::test]
async fn test_parse_plugin_params() {
    let config_content = create_valid_config_yaml();
    let temp_file = create_temp_config_file(&config_content);
    let config_path = temp_file.path().to_str().unwrap();
    
    let config_manager = ConfigManager::new(config_path).await.expect("Failed to load config");
    let config: Config = config_manager.get_config().await;
    
    // Verify plugin parameter parsing
    let tpm_plugin = &config.plugins[0];
    assert_eq!(tpm_plugin.name, "tpm-plugin");
    
    if let Some(PluginParams::Tpm(tpm_config)) = &tpm_plugin.params {
        assert_eq!(tpm_config.tpm_base.tcti_config, "device:/dev/tpm0");
        assert_eq!(tpm_config.event_log_path, "/sys/kernel/security/tpm0/binary_bios_measurements");
    } else {
        panic!("Expected TPM plugin params");
    }
    
    let ima_plugin = &config.plugins[1];
    if let Some(PluginParams::Ima(ima_config)) = &ima_plugin.params {
        assert_eq!(ima_config.log_path, "/sys/kernel/security/ima/ascii_runtime_measurements");
        assert!(ima_config.ascii_runtime_measurements);
    } else {
        panic!("Expected IMA plugin params");
    }
}